﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace gt.framework.Security
{
    public class RSACryptorapher:ICryptography
    {
        // Fields
        private string rsaKey = "<RSAKeyValue><Modulus>wOq0QHjSnNmS6qAySWCYhWhMfWZHyCz+u2kTdFSboVoRgAH4T+wobLydGXUVdi2XccJwjvZcPHOZ5vZpYY9Hf9fkpJfxpOwaIB2IV+owq0EFyCdhE7vTFHiZm2cfCo+T8m224KHrMEFsoAhd11eQzyhXIU2K7XHiX5Xu2Jtnn1s=</Modulus><Exponent>AQAB</Exponent><P>9g91q1gltBev0vWlfdkElVXcV7TIu99/nHo5DE5wDDQPGO2Fmtfy02rWlc1G9pm67xcdCgPQ8wKbJ1JuYPY99Q==</P><Q>yLWuJ2/R5Levg3h8f2RZ2EhnyN3+ht7t0sFtdKSBOroU8Mgtvsu6FGkYQdihqN3+mbe3nICq/GuROvg3MUVGDw==</Q><DP>XlYTAPwsiF1EdZbkOdmIHlDqx11yUEUhwbZCROuVnbgfyajWvkTovhGJ76jh+g16U8wCwCIya9il7291DguaOQ==</DP><DQ>AfEQAD2qsCW+wuzVd34HCHqa1myfW7qoXlOUtX4p6eGG9lVZa/EYmb3yiCCKX9HV9rK6Sf9MqCh6PTHNhuJ+rQ==</DQ><InverseQ>yye+av62y1KJrvhUGtw5wqY0rBW8aCwywlqAy4+gOU8OsoNpzSW5j1rLNz7vZxCv/smrVDPm2hvJN745Ln3yow==</InverseQ><D>s/xFn8EZ/myfvXcoc31Dz3O3qWc7oW8ZWhB2rhoh+S/nE97CpQ5XyNtQVuf91fxDR0d5bGg9NclE1U8gknzy3prh4WoRsDv9ik44Dge8FvlFotAWuRJeSlla55m3mv9EcoKq9mxxDAMTin1Bnd70yE/HAnzybgSgFQUhNNxh4kE=</D></RSAKeyValue>";

        #region ICryptography 成员

        public string Decrypt(string encryptedString)
        {
            byte[] rgb = Convert.FromBase64String(encryptedString);
            RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
            provider.FromXmlString(this.rsaKey);
            byte[] bytes = provider.Decrypt(rgb, false);
            return Encoding.Unicode.GetString(bytes);
        }

        public string Encrypt(string normalString)
        {
            byte[] bytes = Encoding.Unicode.GetBytes(normalString);
            RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
            provider.FromXmlString(this.rsaKey);
            byte[] inArray = provider.Encrypt(bytes, false);
            return Convert.ToBase64String(inArray, 0, inArray.Length);
        }

        #endregion

        public static byte[] Sign(byte[] data, RSAPublicKey publicKey)
        {
            HashAlgorithm hash = new SHA256Managed();
            byte[] hashData = hash.ComputeHash(data);
            int blockSize = publicKey.Modulus.Length - 1;

            return Compute(hashData, publicKey, blockSize);
        }

        public static bool Verify(byte[] data, RSAPrivateKey privateKey, byte[] signature)
        {
            HashAlgorithm hash = new SHA256Managed();
            byte[] hashData = hash.ComputeHash(data);
            int blockSize = privateKey.Modulus.Length;
            byte[] signatureHashData = Compute(signature, privateKey, blockSize);

            if (signatureHashData != null && signatureHashData.Length == hashData.Length)
            {
                for (int i = 0; i < signatureHashData.Length; i++)
                {
                    if (signatureHashData[i] != hashData[i])
                    {
                        return false;
                    }
                }
                return true;
            }

            return false;
        }

        private static byte[] Compute(byte[] data, RSAPublicKey publicKey, int blockSize)
        {
            //
            // 公钥加密/解密公式为：ci = mi^e ( mod n )          
            //
            // 先将 m（二进制表示）分成数据块 m1, m2, ..., mi ，然后进行运算。
            //
            BigInteger e = new BigInteger(publicKey.Exponent);
            BigInteger n = new BigInteger(publicKey.Modulus);

            int blockOffset = 0;
            using (MemoryStream stream = new MemoryStream())
            {
                while (blockOffset < data.Length)
                {
                    int blockLen = Math.Min(blockSize, data.Length - blockOffset);
                    byte[] blockData = new byte[blockLen];
                    Buffer.BlockCopy(data, blockOffset, blockData, 0, blockLen);

                    BigInteger mi = new BigInteger(blockData);
                    BigInteger ci = mi.modPow(e, n);//ci = mi^e ( mod n )

                    byte[] block = ci.getBytes();
                    stream.Write(block, 0, block.Length);
                    blockOffset += blockLen;
                }

                return stream.ToArray();
            }
        }

        private static byte[] Compute(byte[] data, RSAPrivateKey privateKey, int blockSize)
        {
            //
            // 私钥加密/解密公式为：mi = ci^d ( mod n )
            //
            // 先将 c（二进制表示）分成数据块 c1, c2, ..., ci ，然后进行运算。          
            //
            BigInteger d = new BigInteger(privateKey.D);
            BigInteger n = new BigInteger(privateKey.Modulus);

            int blockOffset = 0;

            using (MemoryStream stream = new MemoryStream())
            {
                while (blockOffset < data.Length)
                {
                    int blockLen = Math.Min(blockSize, data.Length - blockOffset);
                    byte[] blockData = new byte[blockLen];
                    Buffer.BlockCopy(data, blockOffset, blockData, 0, blockLen);

                    BigInteger ci = new BigInteger(blockData);
                    BigInteger mi = ci.modPow(d, n); ;//mi = ci^d ( mod n )

                    byte[] block = mi.getBytes();
                    stream.Write(block, 0, block.Length);
                    blockOffset += blockLen;
                }

                return stream.ToArray();
            }
        }
    }
}
